/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package hextd.util;

import hextd.hex.Hex;
import hextd.hex.HexBase;
import hextd.hex.HexEmpty;
import hextd.hex.HexPath;
import hextd.wave.Path;
import hextd.wave.PathNormal;
import hextd.wave.Wave;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.security.NoSuchAlgorithmException;
import java.util.Iterator;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author hylke
 */
public class MapIO {

    private static MD5 md5;

    public static String generateMapMD5(Context c) {
        if (MapIO.md5 == null) {
            try {
                MapIO.md5 = MD5.getInstance();
            } catch (NoSuchAlgorithmException ex) {
                Logger.getLogger(MapIO.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        if (MapIO.md5 != null) {
            return MapIO.md5.hashData(MapIO.printMap(c));
        } else {
            return "";
        }
    }

    public static boolean readLevel(Context c, String mapLocation) {
        URL url;
        boolean success = true;
        c.mapMD5 = "";
        try {
            url = new URL(c.mapContextUrl, mapLocation);
            MapIO.readLevel(c, url, mapLocation);
        } catch (MalformedURLException ex) {
            System.err.println("MapIO::readLevel: loading failed, context=" + c.mapContextUrl + " map=" + mapLocation);
            ex.printStackTrace();
            success = false;
        }
        // Check if md5 already calculated. If not, do it now.
        if (c.mapMD5.length() == 0) {
            c.mapMD5 = MapIO.generateMapMD5(c);
        }
        return success;
    }

    public static void readLevel(Context c, URL fetchUrl, String relativeLoc) {
        BufferedReader bufferedreader;

        try {
            //System.out.println("HexTD::FetchFromUrl::file = " + fetchUrl.toString());
            URLConnection urlconnection = fetchUrl.openConnection();
            if (urlconnection.getContentEncoding() != null) {
                bufferedreader = new BufferedReader(new InputStreamReader(urlconnection.getInputStream(), urlconnection.getContentEncoding()));
            } else {
                bufferedreader = new BufferedReader(new InputStreamReader(urlconnection.getInputStream(), "utf-8"));
            }
        } catch (IOException _ex) {
            System.err.println("HexTD::readFile:: Can't read from " + fetchUrl);
            return;
        }
        if (relativeLoc == null) {
            relativeLoc = fetchUrl.toString();
        }
        MapIO.readLevel(c, bufferedreader, relativeLoc);
    }

    public static void readLevel(Context context, BufferedReader b, String location) {
        // We should not remove lots of graphical elements while painting is in progress, thus lock:
        synchronized (context.PaintingLock) {
            context.mapLocation = location;

            //this.gameBoard.setVisible(true);
            //this.jPanel_GameWon.setVisible(false);
            //this.jPanel_GameLost.setVisible(false);

            context.clearWaves();
            Vector<Wave> waves = context.getWaves();

            Vector<Path> paths = context.getPaths();
            paths.clear();
            context.livesStart = 20;
            context.interestIncrease = 3;
            context.interestStart = 3;
            context.levelInfo = "";
            context.levelTitle = "";
            context.backgroundImage = "";

            String sLine;
            char c;
            int i;
            int count;
            int lineNr = 0;
            try {

                boolean next = true;
                int current = 0;
                long baseHealth = 100;
                int basePrice = 5;
                int pathNr = 0;
                PathNormal currentPath = null;
                Wave currentWave = null;
                String[] strings;

                while (next && (sLine = b.readLine()) != null) {
                    if (sLine.trim().length() != 0) {
                        switch (current) {
                            case 4:
                                context.levelInfo += sLine + "\n";
                                break;
                            case 3:
                                // We're reading a board
                                count = 0;
                                for (i = 0; i < sLine.length(); i++) {
                                    c = sLine.charAt(i);
                                    if (c != ' ') {
                                        if (count <= context.grid.length - (lineNr & 1)) {
                                            switch (c) {
                                                case 'b':
                                                    context.grid[count][lineNr] = new HexBase(count, lineNr, context, true);
                                                    break;
                                                case 'e':
                                                case 'p':
                                                    context.grid[count][lineNr] = new HexPath(count, lineNr, context);
                                                    break;
                                                case 'n':
                                                    context.grid[count][lineNr] = new HexEmpty(count, lineNr, context);
                                                    break;
                                                default:
                                                    System.err.println("HexTD::readFile:: Unknown hex code: " + c);
                                                    break;
                                            }
                                        }
                                        count++;
                                    }
                                }
                                lineNr++;
                                break;
                            case 2:
                                // We're reading a wave
                                strings = sLine.split(" ");
                                if (strings.length > 0) {
                                    currentWave.addEnemiesFromNames(strings, pathNr);
                                    pathNr++;
                                } else {
                                    System.err.println("Warning, empty wave: " + strings.length + " enemies");
                                }
                                break;
                            case 1:
                                // We're reading a path
                                strings = sLine.split(",");
                                if (strings.length == 2) {
                                    try {
                                        Integer x = new Integer(strings[0]);
                                        Integer y = new Integer(strings[1]);
                                        currentPath.addStep(x.intValue(), y.intValue());
                                    } catch (NumberFormatException ex) {
                                        System.err.println("Warning, not a number in path!");
                                        ex.printStackTrace();
                                    }
                                } else {
                                    System.err.println("Warning, error reading path, expected 2 but got " + strings.length + " values");
                                }
                                break;
                            case 0:
                                break;
                            default:
                                System.err.println("Warning, unknown state (" + current + ") reading map");
                                break;
                        }
                    }
                    if (sLine.trim().length() == 0) {
                        switch (current) {
                            case 1:
                                // end path
                                currentPath.finalise();
                                paths.add(currentPath);
                                break;
                            case 2:
                                // end wave
                                waves.add(currentWave);
                                break;
                        }
                        current = 0;
                    }
                    if (current == 0) {
                        if (sLine.startsWith("path")) {
                            // start a new path
                            strings = sLine.split(" ");
                            float baseDelay = 0;
                            if (strings.length == 2) {
                                try {
                                    baseDelay = new Float(strings[1]).floatValue();
                                } catch (NumberFormatException ex) {
                                    System.err.println("Warning, not a number in path baseDelay value!");
                                    ex.printStackTrace();
                                }
                            }
                            current = 1;
                            currentPath = new PathNormal(context, baseDelay);

                        } else if (sLine.startsWith("wave")) {
                            // start a new wave
                            current = 2;
                            pathNr = 0;
                            strings = sLine.split(" ");
                            if (strings.length == 3) {
                                try {
                                    baseHealth = new Long(strings[1]).longValue();
                                    basePrice = new Integer(strings[2]).intValue();
                                } catch (NumberFormatException ex) {
                                    System.err.println("Warning, not a number in wave health value!");
                                    ex.printStackTrace();
                                }
                                currentWave = new Wave(context, baseHealth, basePrice, waves.size() + 1);
                            } else {
                                System.err.println("Warning, error reading wave, expected 'wave <health> <price>' but got " + (strings.length - 1) + " values");
                            }
                        } else if (sLine.startsWith("level")) {
                            // start of a new level!
                            current = 3;
                            context.levelTitle = sLine.substring(6);
                            sLine = b.readLine();
                            context.gridWidth = new Integer(sLine).intValue();

                            sLine = b.readLine();
                            context.gridHeight = new Integer(sLine.trim()).intValue();

                            context.grid = new Hex[context.gridWidth][context.gridHeight];

                            lineNr = 0;
                        } else if (sLine.startsWith("budget")) {
                            // start of a new level!
                            strings = sLine.split(" ");
                            if (strings.length >= 2) {
                                try {
                                    int budget = new Integer(strings[1]).intValue();
                                    context.creditsStart = budget;
                                } catch (NumberFormatException ex) {
                                    System.err.println("Warning, not a number in budget value!");
                                    ex.printStackTrace();
                                }
                            } else {
                                System.err.println("Warning, error reading budget, expected 'budget <budget> <baseInterest> <interestIncrease>' but got " + (strings.length - 1) + " values");
                            }
                            if (strings.length >= 4) {
                                try {
                                    context.interestStart = new Integer(strings[2]).intValue();
                                    context.interestIncrease = new Integer(strings[3]).intValue();
                                } catch (NumberFormatException ex) {
                                    System.err.println("Warning, not a number in budget value!");
                                    ex.printStackTrace();
                                }
                            }
                        } else if (sLine.startsWith("lives")) {
                            // start of a new level!
                            strings = sLine.split(" ");
                            if (strings.length == 2) {
                                try {
                                    context.livesStart = new Integer(strings[1]).intValue();
                                    context.resetLives(context.livesStart);
                                } catch (NumberFormatException ex) {
                                    System.err.println("Warning, not a number in budget value!");
                                    ex.printStackTrace();
                                }
                            } else {
                                System.err.println("Warning, error reading lives, expected 'lives <lives>' but got " + (strings.length - 1) + " values");
                            }
                        } else if (sLine.startsWith("background")) {
                            // start of a new level!
                            context.backgroundImage = sLine.substring(11);
                        } else if (sLine.startsWith("info")) {
                            // Level info following
                            current = 4;
                        }
                    }

                }
                switch (current) {
                    case 1:
                        // end path
                        currentPath.finalise();
                        paths.add(currentPath);
                        break;
                    case 2:
                        // end wave
                        waves.add(currentWave);
                        break;
                }

                b.close();
            } catch (IOException e) {
                System.out.println("HexTD::readFile::IO exception: " + e.getLocalizedMessage());
                return;
            // Do something with the error here.
            }
        //this.jPanelLevelSelector.setLevelInfo("<h1>" + this.levelTitle + "</h1>" + this.levelInfo);
        //this.jCheckBox_autoLaunch.setSelected(false);
        //this.recalculateScale();
        }
    //this.context.resetMap();
    //this.blackBoxRecord.clear();
    //this.updateInfo();
    }

    public static String printMap(Context c) {
        String retval = "level " + c.levelTitle + "\n";
        retval += "" + c.gridWidth + "\n";
        retval += "" + c.gridHeight + "\n";
        for (int y = 0; y < c.grid[0].length; y++) {
            if ((y & 1) == 1) {
                retval += " ";
            }
            int xLength = c.grid.length - (y & 1);
            for (int x = 0; x < xLength; x++) {
                retval += c.grid[x][y].getShortName() + " ";
            }
            retval += "\n";
        }
        retval += "\n";
        retval += "info\n";
        retval += c.levelInfo + "\n";
        retval += "\n";
        Vector<Path> paths = c.getPaths();
        for (int i = 0; i < paths.size(); i++) {
            Path tempPath = paths.get(i);
            retval += "path " + tempPath.getBaseDelay() + "\n";
            int[] step;
            for (int s = 0; s < tempPath.length(); s++) {
                step = tempPath.getStepHexCoords(s);
                retval += "" + step[0] + "," + step[1] + "\n";
            }
            retval += "\n";
        }

        retval += "budget " + c.creditsStart + " " + c.interestStart + " " + c.interestIncrease + "\n";
        retval += "\n";
        retval += "lives " + c.livesStart + "\n";
        retval += "\n";
        retval += "background " + c.backgroundImage + "\n";
        retval += "\n";

        for (Iterator<Wave> i = c.getWaves().iterator(); i.hasNext();) {
            Wave w = i.next();
            retval += "wave " + w.getBaseHealth() + " " + w.getBasePrice() + "\n";
            for (Iterator<String[]> ins = w.getNameStrings().iterator(); ins.hasNext();) {
                String[] strings = ins.next();
                for (int j = 0; j < strings.length; j++) {
                    retval += strings[j] + " ";
                }
                retval += "\n";
            }
            retval += "\n";
        }

        return retval;
    }
}
